#include "bg_scene.h"
#include "bg_element.h"
#include "bg_editor.h"
#include "bg_path.h"
#include "bg_waypoint.h"
#include "application/game_script.h"

#include "script/scripthelper.h"
#include "script/write_lua.h"
#include "graphics/graphics.h"
#include "graphics/graphics_util.h"
#include "editor/editor_selection.h"

#include "ui/ui_manager.h"
#include "util/timer.h"				// log
#include "util/log.h"				// log
#include "util/common_macros.h"		// macros
#include "util/mmgr.h"		    

#include <cassert>
#include <algorithm>

#ifdef _ENABLE_LUABIND
#include <luabind/luabind.hpp>
#endif

using namespace background;
using namespace script;

namespace {	// empty namespace
/////////////////////////////
// Custom component factory
class	CustomComponentFactory
{
public:
	void			AddComponent(const std::string& Name, int	KeyID, BGScene::CreateChunkCB					pCreateCB, editor::EditorMain::CreateBrushCB		pBrushCB)
	{
		if (m_Set.GetElement(Name)) return;	// already existing
		CreateComponentData*	pDef = new CreateComponentData;
		pDef->Name = Name;
		pDef->pCreateCB = pCreateCB;
		pDef->pBrushCB = pBrushCB;
		pDef->KeyID = KeyID;
		m_Set.AddElement(Name, pDef);
	}
	BGElement*		GetComponent(const std::string& Name, BGScene* pScene)
	{
		CreateComponentData* pDef = m_Set.GetElement(Name);
		if (pDef && pDef->pCreateCB)
		{
			BGElement*	pElement = pDef->pCreateCB(pScene);
			if (pElement) pElement->SetupCustom(Name);
			return pElement;
		}
		return NULL;
	}	
	void			RegisterWithEditor(BGEditor* pEditor)
	{
		for (unsigned int i=0; i<m_Set.GetElementC(); i++)
		{
			CreateComponentData* pDef = m_Set.GetElementByIndex(i);
			if (pDef && pDef->pBrushCB)
			{
				pEditor->AddBrush(pDef->Name, pDef->KeyID, pDef->pBrushCB);
			}
		}
	}
	static CustomComponentFactory&	Instance()
	{
		static CustomComponentFactory Inst;
		return Inst;
	}
private:
	CustomComponentFactory(){};
	~CustomComponentFactory() {};

	struct		CreateComponentData
	{
		std::string								Name;
		int										KeyID;
		BGScene::CreateChunkCB					pCreateCB;
		editor::EditorMain::CreateBrushCB		pBrushCB;
	};
	num::NamedSet<CreateComponentData*>				m_Set;

};
}	// namespace 




math::Vec2	BGScene::s_BaseScreen(1024, 768);

math::Vec2	BGScene::ConvertFromFile(const math::Vec2& Pos)
{
	return math::Vec2(Pos.x * s_BaseScreen.x * GraphicsUtil::InvW, Pos.y * s_BaseScreen.y * GraphicsUtil::InvH); 
}

BGScene::BGScene() : 
	m_Min(0,0),
	m_Max(1,1),
	m_pEditor(NULL),
	m_PathIndex(0),
	m_TileA(NULL),
	m_Width(9),
	m_Height(9),
	m_TileSize(512/(float)GraphicsUtil::WIDTH, 512/(float)GraphicsUtil::HEIGHT),
	m_GridSize(32/(float)GraphicsUtil::WIDTH, 32/(float)GraphicsUtil::HEIGHT),
	m_Scale(1),
	m_ActivationKey(KEY_F2),
	m_Wrapping(false),
	m_AspectScale(1.0f),
	m_IsFirstTick(true)
{
}

BGScene::~BGScene()
{
	DeleteObjects();
	UTIL_SAFE_DELETEA(m_TileA);
}



void				BGScene::FlushImages()
{
	// flush all background tiles
	if (m_TileA)
	{
		for (int i=0; i<m_Width*m_Height; i++)
			m_TileA[i].Background.FlushImage();	// drop the data
	}
	// flush all sprites loaded in other ways
	for (unsigned int i=0; i<m_BackElements.size(); i++)
	{
		m_BackElements[i]->FlushImages();
	}
	for (unsigned int i=0; i<m_FrontElements.size(); i++)
	{
		m_FrontElements[i]->FlushImages();
	}
}

void				BGScene::Tick(float dt)
{
	
	SetOffset(GetOffset());
	m_Background.Tick(dt);
	if (m_TileA)
	{
		for (int i =0; i<m_Width*m_Height; i++)
		{
			m_TileA[i].Background.Tick(dt);
			
		}

		if(m_IsFirstTick)
		{
			for (int i =0; i<m_Width*m_Height; i++)
			{
				math::Vec2	Screen;
				m_TileA[i].Background.GetScreenSize(Screen);
				Screen.x *=(float)GraphicsUtil::WIDTH;	// convert them back to pixel coordinates
				Screen.y *=(float)GraphicsUtil::HEIGHT;
				m_TileA[i].Background.m_UV1.x = 1.0f / Screen.x;
				m_TileA[i].Background.m_UV1.y = 1.0f / Screen.y;
				m_TileA[i].Background.m_UV2.x = 1.0f - (1.0f / Screen.x);		
				m_TileA[i].Background.m_UV2.y = 1.0f - (1.0f / Screen.y);
				m_TileA[i].Background.m_QuadRotation = (ui::UISprite::ROTATION)m_TileA[i].Rot;
			}
			m_IsFirstTick = false;
		}

	}
	


	for (unsigned int i=0; i<m_BackElements.size(); i++)
	{
		BGElement* pElement = m_BackElements[i];
		if (pElement->IsVisible(m_Offset, m_Scale))
		{
			pElement->Tick(dt);
		}
	}
	for (unsigned int i=0; i<m_FrontElements.size(); i++)
	{
		BGElement* pElement = m_FrontElements[i];
		if (pElement->IsVisible(m_Offset, m_Scale))
		{
			pElement->Tick(dt);
		}
	}
	if (m_pEditor) m_pEditor->Tick(dt);
}

void				BGScene::RenderBackground(float	Scale)
{
	m_Scale = Scale;
	int	StartForeground = 0;

	// setup the background 
	if (m_TileA)
	{
		// for each tile in view
		int	cx = int(m_Offset.x / m_TileSize.x);
		int	cy = int(m_Offset.y / m_TileSize.y);
		int	dx = 1+int(1.0f / m_TileSize.x);
		int	dy = 1+int(1.0f / m_TileSize.y);
		for (int x=cx-dx; x<=cx+dx+1; x++)
		for (int y=cy-dy; y<cy+dy+1; y++)
		{

			int	tx = abs(x) % m_Width;
			int ty = abs(y) % m_Height;
			int	Index = tx + (ty*m_Width);
			if (!m_TileA[Index].Background.IsValid()) continue;

			math::Vec2	From(x*m_TileSize.x, y*m_TileSize.y);
			math::Vec2	To = From + m_TileSize;
			if ((From.x-m_Offset.x)*Scale < 1 && (From.y-m_Offset.y)*Scale < 1 && 
				(To.x-m_Offset.x)*Scale > 0 && (To.y-m_Offset.y)*Scale > 0)
			{
				m_TileA[Index].Background.m_Pos = (From - m_Offset)*Scale;
				m_TileA[Index].Background.m_Size = m_TileSize*Scale;
				m_TileA[Index].Background.Render();
			}
		}
	}

	if (Image* pImage = m_Background.GetImage())
	{
		// UVs wrap naturally
		m_Background.m_UV1.x = (m_Offset.x * (float)GraphicsUtil::WIDTH) / (float)pImage->GetWidth() ;
		m_Background.m_UV1.y = (m_Offset.y * (float)GraphicsUtil::HEIGHT) / (float)pImage->GetHeight() ;
		m_Background.m_UV2 = m_Background.m_UV1 + math::Vec2((float)GraphicsUtil::WIDTH/((float)pImage->GetWidth()*Scale), (float)GraphicsUtil::HEIGHT/((float)pImage->GetHeight()*Scale));
		m_Background.m_Size = math::Vec2(1,1);
		m_Background.Render();
	}

	for (unsigned int i=0; i<m_BackElements.size(); i++)
	{
		BGElement* pElement = m_BackElements[i];
		if (pElement->IsVisible(m_Offset, Scale))
			pElement->Render(m_Offset, Scale);
	}

}


void				BGScene::RenderForeground(float	Scale)
{
	m_Scale = Scale;
	if (m_pEditor && m_pEditor->GetSelection() && m_pEditor->GetSelection()->IsVisible())
	{
		// otherwise we'll render these over the UI!
	} else
	{
		for (unsigned int i=0; i<m_FrontElements.size(); i++)
		{
			BGElement* pElement = m_FrontElements[i];
			if (pElement->IsVisible(m_Offset, Scale))
			{
				pElement->Render(m_Offset, Scale);
			}
		}
	}
	if (m_pEditor)
	{
		m_pEditor->Render(m_Offset, Scale);
		if (m_pEditor->ShowDebug())
		{
			RenderDebug(m_Offset, Scale);
		}
	}
}



void				BGScene::RenderDebug(const math::Vec2& m_Offset, float Scale)
{
	for (unsigned int i=0; i<m_FrontElements.size(); i++)
	{
		m_FrontElements[i]->RenderDebug(m_Offset, Scale);
	}
	for (unsigned int i=0; i<m_BackElements.size(); i++)
	{
		m_BackElements[i]->RenderDebug(m_Offset, Scale);
	}
	for (unsigned int i=0; i<m_Paths.size(); i++)
	{
		m_Paths[i]->RenderDebug(m_Offset, Scale);
	}
	for (unsigned int i=0; i<m_Waypoints.size(); i++)
	{
		m_Waypoints[i]->RenderDebug(m_Offset, Scale);		
	}
}


void				BGScene::SaveFile(script::WriteLua& WL)
{
	WL.StartTable("Info");

		WL.WritePair("AspectRatio",  (float)GetGraphics()->GetScreenWidth() / (float)GetGraphics()->GetScreenHeight());
		WL.WritePair("BackgroundImg", m_BackgroundImg);
		WL.WritePair("Min", m_Min);
		WL.WritePair("Max", m_Max);
		WL.WritePair("Width", m_Width);
		WL.WritePair("Height", m_Height);
		WL.WritePair("TileSize", m_TileSize / m_AspectScale);
		
	WL.CloseTable();

	WL.StartTable("Background");
		for (unsigned int i=0; i<m_FrontElements.size(); i++)
		{
			WL.StartTable();
			m_FrontElements[i]->Save(&WL);
			WL.CloseTable(true);
		}
		for (unsigned int i=0; i<m_BackElements.size(); i++)
		{
			WL.StartTable();
			m_BackElements[i]->Save(&WL);
			WL.CloseTable(true);
		}
	WL.CloseTable();

	WL.StartTable("Paths");
	for (unsigned int i=0; i<m_Paths.size(); i++)
	{
		BGPath* pPath = m_Paths[i];
		const math::SplineCache2D& Spline = pPath->GetSpline();
		WL.StartTable();
			WL.WritePair("ID", pPath->GetID());
			WL.StartTable("Nodes");
				for (int j=0; j<(int)Spline.GetNodeC(); j++)
				{
					WL.Write("{ %f, %f }, \n", Spline.GetNode(j)->Pos.x, 	Spline.GetNode(j)->Pos.y);
				}
			WL.CloseTable();
		WL.CloseTable();
	}
	WL.CloseTable();

	if (m_TileA)
	{
		WL.StartTable("BackgroundTiles");
			for (int i =0; i<m_Width*m_Height; i++)
			{
			WL.StartTable();
				WL.WritePair("Img", m_TileA[i].BackgroundImg);
				WL.WritePair("x", m_TileA[i].x);
				WL.WritePair("y", m_TileA[i].y);
				WL.WritePair("Rot", m_TileA[i].Rot);
			WL.CloseTable();
			}
		WL.CloseTable();
	}	
	
	if (m_Waypoints.size() > 0)
	{
		WL.StartTable("Waypoints");
		for (unsigned int i=0; i<m_Waypoints.size(); i++)
		{
			WL.StartTable();
				WL.WritePair("Pos", m_Waypoints[i]->GetPos());
				WL.WritePair("ID", m_Waypoints[i]->GetID());
			WL.CloseTable();
		}
		WL.CloseTable();
	}
}

bool				BGScene::Save(const std::string& File)
{
	if (m_pEditor) m_pEditor->SetBrush(NULL);

	WriteLua	WL;
	WL.Open(File);
	WL.StartTable("BGScene");
	SaveFile(WL);
	WL.CloseTable(false);
	return true;

}

BGScene*			BGScene::GetSubScene(const math::Vec2& From, const math::Vec2& To)
{
	BGScene*	pNew = new BGScene();
	pNew->SetMax(To-From);
	pNew->SetBackgroundTile(0, 0, math::Vec2(0,0));
	
	// do we clone backgrounds, tiles, etc? That could get messy in regards to 
	// accurate offsets, etc
	// only copy elements for now.

	for (unsigned int i=0; i<m_FrontElements.size(); i++)
	{
		BGElement* pElement = m_FrontElements[i];
		if (pElement->InsideRegion(From, To))
		{
			BGElement* pClone = pElement->Clone();
			pClone->SetPos(pClone->GetPos() - To);	// correct m_Offset
			pNew->AddElement(pClone);
		}
	}
	for (unsigned int i=0; i<m_BackElements.size(); i++)
	{
		BGElement* pElement = m_BackElements[i];
		if (pElement->InsideRegion(From, To))
		{
			BGElement* pClone = pElement->Clone();
			pClone->SetPos(pClone->GetPos() - To);	// correct m_Offset
			pNew->AddElement(pClone);
		}
	}

	return pNew;
}


void				BGScene::SetBackground(const std::string& File)
{
	UTIL_SAFE_DELETEA(m_TileA);
	m_BackgroundImg = File;
	if (m_BackgroundImg.empty())
	{
		m_Background.Invalidate();
		return;
	}
	GameScript::StripFilePath(m_BackgroundImg);
	m_Background.Create(File, math::Vec2(0,0));
	Image*	pImage = m_Background.GetImage();
	if (pImage)
	{
		m_Background.m_UV1 = math::Vec2(0,0);
		m_Background.m_UV2 = math::Vec2((float)GraphicsUtil::WIDTH/(float)pImage->GetWidth(), (float)GraphicsUtil::HEIGHT/(float)pImage->GetHeight());
	 
	}
	m_Min = math::Vec2(0,0);
	m_Background.GetScreenSize(m_Max);

}

void				BGScene::ClearTiles()
{
	m_Width = 0;
	m_Height = 0;
	UTIL_SAFE_DELETEA(m_TileA);
}


void				BGScene::SetBackgroundTile(int Width, int Height, const math::Vec2& TileSize)
{
	int OldWidth = m_Width;
	int OldHeight = m_Height;
	math::Vec2 OldTileSize = m_TileSize;
	BgTile* pOldTileA = m_TileA;

	m_Width = Width;
	m_Height = Height;
	m_TileSize = TileSize;
	m_TileA = new BgTile[m_Width*m_Height];

	for (int x = 0; x < m_Width; x++)
	for (int y = 0; y < m_Height; y++)
	{
		int Index = x + (y * m_Width);
		if (pOldTileA && (x < OldWidth) && (y < OldHeight))
		{
			int OldIdx = x + (y * OldWidth);
			m_TileA[Index].x = pOldTileA[OldIdx].x;
			m_TileA[Index].y = pOldTileA[OldIdx].y;
			m_TileA[Index].Rot = pOldTileA[OldIdx].Rot;
			m_TileA[Index].BackgroundImg = pOldTileA[OldIdx].BackgroundImg;
			if (pOldTileA[OldIdx].Background.IsValid())
				m_TileA[Index].Background.Create( pOldTileA[OldIdx].Background.GetFilename() );
		}
		else
		{
			m_TileA[Index].x = x;
			m_TileA[Index].y = y;
			m_TileA[Index].Rot = 0;
		}
	}

	UTIL_SAFE_DELETEA(pOldTileA);
	m_Min = math::Vec2(0,0);
	m_Max = math::Vec2(Width*TileSize.x , Height*TileSize.y);

	/*
	ClearTiles();
	m_Width = Width;
	m_Height = Height;
	m_TileSize = TileSize;
	m_TileA = new BgTile[m_Width*m_Height];
	for (int x=0; x<m_Width; x++)
	for (int y=0; y<m_Height; y++)
	{
		int	Index = x + (y*m_Width);
		m_TileA[Index].x = x;
		m_TileA[Index].y = y;
	}
	m_Min = math::Vec2(0,0);
	m_Max = math::Vec2(Width*TileSize.x, Height*TileSize.y);
	*/
}

void				BGScene::SetTile(int x, int y, const std::string& File, int Rotation)
{
	if (x>=0 && y>=0 && x<m_Width && y<m_Height && m_TileA)
	{
		int	Index = x + (y*m_Width);
		m_TileA[Index].BackgroundImg = File;
		m_TileA[Index].Background.Create(File);
		m_TileA[Index].Rot = Rotation;

		/*
		// we need to Tick first to get screensize
		math::Vec2	Screen;
		m_TileA[Index].Background.GetScreenSize(Screen);
		Screen.x *=(float)GraphicsUtil::WIDTH;	// convert them back to pixel coordinates
		Screen.y *=(float)GraphicsUtil::HEIGHT;
		m_TileA[Index].Background.m_UV1.x = 1.0f / Screen.x;
		m_TileA[Index].Background.m_UV1.y = 1.0f / Screen.y;
		m_TileA[Index].Background.m_UV2.x = 1.0f - (1.0f / Screen.x);		
		m_TileA[Index].Background.m_UV2.y = 1.0f - (1.0f / Screen.y);
		m_TileA[Index].Background.m_QuadRotation = (ui::UISprite::ROTATION)Rotation;
		*/

	}
}


void				BGScene::MaxFromBG()
{
	if (m_Width > 0 && m_Height > 0)
	{
		m_Min = math::Vec2(0,0);
		m_Max = math::Vec2(m_TileSize.x * m_Width, m_TileSize.y * m_Height);
	} else
	if (m_Background.IsValid())
	{
		m_Min = math::Vec2(0,0);
		m_Max = math::Vec2(m_Background.GetImage()->GetWidth()/(float)GraphicsUtil::WIDTH, m_Background.GetImage()->GetHeight()/(float)GraphicsUtil::HEIGHT) ;	
	}
}
void				BGScene::LoadFile(LuaPlus::LuaObject& Obj, bool LoadBGTiles)
{
	const float AsepctRatioCurrent = (float)GetGraphics()->GetScreenWidth() / (float)GetGraphics()->GetScreenHeight();

	DeleteObjects();
	if (!Obj.IsTable()) return;
	LuaPlus::LuaObject	Info = Obj.GetByName("Info");
	if (Info.IsTable())
	{
		float CreatedAspectRatio = ScriptHelper::GetTableFloat(Info, "AspectRatio", 0);

		if( CreatedAspectRatio > 0)
		{
			m_AspectScale = CreatedAspectRatio / AsepctRatioCurrent;
		}

		m_BackgroundImg = ScriptHelper::GetTableString(Info, "BackgroundImg", "");
		m_Min			= ScriptHelper::GetTableVec2(Info, "Min", math::Vec2(0,0));
		m_Max			= ScriptHelper::GetTableVec2(Info, "Max", math::Vec2(1,1));
		m_TileSize		= ScriptHelper::GetTableVec2(Info, "TileSize", m_TileSize);
		m_TileSize.x *= m_AspectScale;

		int newWidth	= ScriptHelper::GetTableInt(Info, "Width", m_Width);
		int newHeight	= ScriptHelper::GetTableInt(Info, "Height", m_Height);
		if (newWidth > 0 && newHeight > 0 && LoadBGTiles)	
		{
			SetBackgroundTile(newWidth, newHeight, m_TileSize);
			LuaPlus::LuaObject BackgroundTiles = Obj.GetByName("BackgroundTiles");
			if (BackgroundTiles.IsTable() && m_Width > 0 && m_Height > 0)
			{
				for (int i=0; i<BackgroundTiles.GetTableCount(); i++)
				{
					LuaPlus::LuaObject Tile = BackgroundTiles.GetByIndex(i+1);
					if (Tile.IsTable())
					{
						std::string		Img = ScriptHelper::GetTableString(Tile, "Img");
						int				x	= ScriptHelper::GetTableInt(Tile, "x", i % m_Width);
						int				y	= ScriptHelper::GetTableInt(Tile, "y", i / m_Width);
						int				Rot	= ScriptHelper::GetTableInt(Tile, "Rot", 0);
						SetTile(x, y, Img, Rot);
					}
				}
			}

		}
	}
	if (LoadBGTiles)
	{
		if (!m_BackgroundImg.empty())
			SetBackground(m_BackgroundImg);
		else
			m_Background.Invalidate();
	}

	LuaPlus::LuaObject Paths =Obj.GetByName("Paths");
	if (Paths.IsTable())
	{
		for (int i=0; i<Paths.GetTableCount(); i++)
		{
			LuaPlus::LuaObject Path = Paths.GetByIndex(i+1);
			if (Path.IsTable())
			{
				int Index = ScriptHelper::GetTableInt(Path, "ID", -1);
				LuaPlus::LuaObject Nodes = Path.GetByName("Nodes");
				if (Nodes.IsTable())
				{
					math::SplineCache2D	Spline;
					for (int j=0; j<Nodes.GetTableCount(); j++)
					{
						LuaPlus::LuaObject LuaObj = Nodes.GetByIndex(j+1);
						math::Vec2	Node = ConvertFromFile(ScriptHelper::GetVec2(LuaObj));	
						Spline.InsertNode(Node);
					}
					AddPath(Spline, Index);
				}
			}
		}
	}		
	std::sort(m_Paths.begin(), m_Paths.end(), BGPath::Sort);
	m_PathIndex = 0;
	if (m_Paths.size()>0)	m_PathIndex=m_Paths[m_Paths.size()-1]->GetID();

	math::Vec2 TempVec2;
	LuaPlus::LuaObject	Panel = Obj.GetByName("Background");
	if (Panel.IsTable())
	{
		for (int i=0; i<Panel.GetTableCount(); i++)
		{
			LuaPlus::LuaObject	Element = Panel.GetByIndex(i+1);
			BGElement*	pElement = BGElement::LoadElement(this, Element);
			if (pElement)
			{
				TempVec2 = pElement->GetPos();
				TempVec2.x *= m_AspectScale;
				pElement->SetPos( TempVec2 );

				TempVec2 = pElement->GetSize();
				TempVec2.x *= m_AspectScale;
				pElement->SetSize( TempVec2 );

				AddElement(pElement);
			}
		}
	}

	{
		LuaPlus::LuaObject Waypoints = Obj.GetByName("Waypoints");
		if (Waypoints.IsTable())
		{
			for (int i=0; i<Waypoints.GetTableCount(); i++)
			{
					LuaPlus::LuaObject WP = Waypoints.GetByIndex(i+1);
				if (WP.IsTable())
				{
					math::Vec2		Pos		= ConvertFromFile(ScriptHelper::GetTableVec2(WP, "Pos"));	
					Pos.x *= m_AspectScale;
					int				ID		= ScriptHelper::GetTableInt(WP, "ID", -1);	

					AddWaypoint(Pos, ID);
				}
			}
		}
	}
	//std::sort(m_Waypoints.begin(), m_Waypoints.end(), BGWaypoint::Sort);

}

bool				BGScene::Load(const std::string& File)
{
	m_File = File;

	LuaPlus::LuaState*	pScript = ScriptHelper::CreateLuaState();
	if (!ScriptHelper::DoFile(*pScript, File))
	{
		_LOG(MSG_ERROR, "Unable to load Background " << File << "!");
		 LuaPlus::LuaState::Destroy( pScript );
		return false;
	} else
	{
		LuaPlus::LuaObject Info  = pScript->GetGlobal("BGScene");
		LoadFile(Info);
	}
	LuaPlus::LuaState::Destroy( pScript );
	return true;
}

void				BGScene::LoadFile(LuaPlus::LuaState& State, bool LoadBGTiles)
{
	LuaPlus::LuaObject Info  = State.GetGlobal("BGScene");
	LoadFile(Info, LoadBGTiles);
}

void				BGScene::ResetScene()
{
	UTIL_SAFE_DELETE(m_pEditor);
	m_Background.ForceAnimTime(0.0f);
	if (m_TileA)
	{
		for (int i = 0; i < (m_Width*m_Height); i++)
		{
			m_TileA[i].Background.ForceAnimTime(0.0f);
		}
	}
	ResetElements();
}

bool				BGScene::DropElements(const std::string& SubStr)
{
	// delete from foreground
	for (int i=int(m_FrontElements.size())-1; i>=0; i--)
	{
		BGElement*	pElement = m_FrontElements[i];
		if (pElement->MatchSubStr(SubStr))
		{	
			delete pElement;
			m_FrontElements.erase(m_FrontElements.begin()+i);
		}
	}
	// delete from background
	for (int i=int(m_BackElements.size())-1; i>=0; i--)
	{
		BGElement*	pElement = m_BackElements[i];
		if (pElement->MatchSubStr(SubStr))
		{	
			delete pElement;
			m_BackElements.erase(m_BackElements.begin()+i);
		}
	}
	return true;

}


bool				BGScene::DeleteElement(const math::Vec2& Pos)
{
	// delete from foreground
	math::Vec2	m_Offset(0,0);
	math::Vec2	P = Pos;// - GetOffset(false);	// flip it back into screen space, since elements can modify the m_Offset
	for (int i=int(m_FrontElements.size())-1; i>=0; i--)
	{
		BGElement*	pElement = m_FrontElements[i];
		if (pElement->IsActive() && pElement->IsInside(P, m_Offset))
		{	
			delete pElement;
			m_FrontElements.erase(m_FrontElements.begin()+i);
			return true;
		}
	}
	P = Pos;// - GetOffset(true);	// flip it back into screen space, since elements can modify the m_Offset
	// delete from background
	for (int i=int(m_BackElements.size())-1; i>=0; i--)
	{
		BGElement*	pElement = m_BackElements[i];
		if (pElement->IsActive() && pElement->IsInside(P, m_Offset))
		{	
			delete pElement;
			m_BackElements.erase(m_BackElements.begin()+i);
			return true;
		}
	}

	return false;
}


void				BGScene::AddElement(BGElement* pElement)
{
	if (pElement->GetSort() < 0)
	{
		m_BackElements.push_back(pElement);
		std::sort(m_BackElements.begin(), m_BackElements.end(), BGElement::BySortFunc);
	} else
	{
		m_FrontElements.push_back(pElement);
		std::sort(m_FrontElements.begin(), m_FrontElements.end(), BGElement::BySortFunc);
	}
}

bool				BGScene::DropElement(BGElement* pElementToFind)
{
	for (int i=int(m_FrontElements.size())-1; i>=0; i--)
	{
		BGElement*	pElement = m_FrontElements[i];
		if (pElement == pElementToFind)		
		{	
			m_FrontElements.erase(m_FrontElements.begin()+i);
			return true;
		}
	}
	// delete from background
	for (int i=int(m_BackElements.size())-1; i>=0; i--)
	{
		BGElement*	pElement = m_BackElements[i];
		if (pElement == pElementToFind)
		{	
			m_BackElements.erase(m_BackElements.begin()+i);
			return true;
		}
	}

	return false;
}


BGElement*			BGScene::GetElement(const std::string& Name)
{
	for (int i=int(m_FrontElements.size())-1; i>=0; i--)
	{
		BGElement*	pElement = m_FrontElements[i];
		if (strcmpi(pElement->GetName(), Name.c_str())==0) return pElement;
	}
	for (int i=int(m_BackElements.size())-1; i>=0; i--)
	{
		BGElement*	pElement = m_BackElements[i];
		if (strcmpi(pElement->GetName(), Name.c_str())==0) return pElement;
	}
	return NULL;
}

BGElement*			BGScene::GetElement(const math::Vec2& Pos)
{
	math::Vec2 P = Pos - m_Offset;	// flip it back into screen space, since elements can modify the m_Offset
	for (int i=int(m_FrontElements.size())-1; i>=0; i--)
	{
		BGElement*	pElement = m_FrontElements[i];
		if (pElement->IsActive() && pElement->IsInside(P, m_Offset))
		{	
			return pElement;
		}
	}
	// delete from background
	P = Pos - m_Offset;	// flip it back into screen space, since elements can modify the m_Offset
	for (int i=int(m_BackElements.size())-1; i>=0; i--)
	{
		BGElement*	pElement = m_BackElements[i];
		if (pElement->IsActive() && pElement->IsInside(P, m_Offset))
		{	
			return pElement;
		}
	}

	return NULL;
}

void			BGScene::ResetElements()
{
	for (int i = 0; i < (int)m_FrontElements.size(); i++)
	{
		BGElement* pElement = m_FrontElements[i];
		pElement->ResetElement();
	}

	for (int i = 0; i < (int)m_BackElements.size(); i++)
	{
		BGElement* pElement = m_BackElements[i];
		pElement->ResetElement();
	}
}

bool				BGScene::AddPath(const math::SplineCache2D& Spline, int Index)
{
	if (Index == -1) Index = ++m_PathIndex;
	BGPath* pPath = new BGPath(Index, this);
	pPath->CreateFromSpline(Spline);
	m_Paths.push_back(pPath);

	std::sort(m_Paths.begin(), m_Paths.end(), BGPath::Sort);
	return true;
}

BGPath*			BGScene::GetPath(const math::Vec2& Pos)
{
	for (unsigned int i=0; i<m_Paths.size(); i++)
	{
		if (m_Paths[i]->HitTest(Pos))
		{
			return m_Paths[i];
		}
	}
	return NULL;
}

BGPath*			BGScene::GetPath(int ID)
{
	for (unsigned int i=0; i<m_Paths.size(); i++)
	{
		if (m_Paths[i]->GetID() == ID)
		{
			return m_Paths[i];
		}
	}
	return NULL;
}

void			BGScene::SetOffset(const math::Vec2& V) 
{
	math::Vec2	P = V;
	if (!m_Wrapping)
	{
		UTIL_CLAMP(P.x, m_Min.x, m_Max.x-(1.0f/GetScale()));
		UTIL_CLAMP(P.y, m_Min.y, m_Max.y-(1.0f/GetScale()));
	}
	m_Offset  = P;
}

bool				BGScene::UpdateScroll(const math::Vec2& MousePos, float dt)
{
	const	float	ScrollBorderMinX = 0.02f;
	const	float	ScrollBorderMaxX = 1.0f - ScrollBorderMinX ;
	const	float	ScrollBorderMinY = 0.02f;
	const	float	ScrollBorderMaxY = 1.0f - ScrollBorderMinY;
	const	float	ScrollSpeed	 = 0.8f;

	math::Vec2	P = GetOffset();
	if (MousePos.x < ScrollBorderMinX)
	{
		SetOffset(math::Vec2(P.x - (ScrollSpeed*dt), P.y));
	}else 
	if (MousePos.x > ScrollBorderMaxX)
	{
		SetOffset(math::Vec2(P.x + (ScrollSpeed*dt), P.y));
	}

	if (MousePos.y < ScrollBorderMinY)
	{
		SetOffset(math::Vec2(P.x, P.y - (ScrollSpeed*dt)));
	}
	else if (MousePos.y > ScrollBorderMaxY)
	{
		SetOffset(math::Vec2(P.x, P.y + (ScrollSpeed*dt)));
	}
	return true;
}


bool				BGScene::DeletePath(const math::Vec2& Pos)
{
	for (unsigned int i=0; i<m_Paths.size(); i++)
	{
		if (m_Paths[i]->HitTest(Pos))
		{
			delete m_Paths[i];
			m_Paths.erase(m_Paths.begin() + i);
			return true;
		}
	}
	return false;
}

void				BGScene::MouseButtonReleased(input::Mouse::BUTTON_STATE* pKey, const math::Vec2& Pos)
{
	if (ui::UIManager::Instance().MouseInsideActive(Pos)) return;

	if (m_pEditor)	m_pEditor->MouseButtonReleased(pKey, Pos);

}


void				BGScene::MouseButtonPressed(input::Mouse::BUTTON_STATE* pKey, const math::Vec2& Pos)
{
	if (ui::UIManager::Instance().MouseInsideActive(Pos)) return;

	if (m_pEditor)	m_pEditor->MouseButtonPressed(pKey, Pos);

}
void				BGScene::MouseMove(const math::Vec2& From, const math::Vec2& To)
{
	if (m_pEditor)	m_pEditor->MouseMove(From, To);
}
void				BGScene::MouseWheelMove(int WheelInfo, const math::Vec2& Pos)
{
	if (m_pEditor)	m_pEditor->MouseWheelMove(WheelInfo, Pos);
}
void				BGScene::KeyPressed(input::Keyboard::KEY* pKey)
{
	if (pKey->KeyID == m_ActivationKey)
	{
		if (m_pEditor)	
		{
			UTIL_SAFE_DELETE(m_pEditor);
		}
		else
		{
			m_pEditor = new BGEditor(this);
			CustomComponentFactory::Instance().RegisterWithEditor(m_pEditor);	// start up the custom components
//			m_pEditor->SetGrid(m_GridOffset, m_GridSize);
		}
	}
	if (m_pEditor)	m_pEditor->KeyPressed(pKey);
}

void				BGScene::KeyDown(input::Keyboard::KEY* pKey)
{
	if (m_pEditor)	m_pEditor->KeyDown(pKey);
}


void				BGScene::DeleteObjects()
{
	if (m_pEditor) m_pEditor->SetBrush(NULL);
	for (int i=int(m_FrontElements.size())-1; i>=0; i--)
	{
		delete m_FrontElements[i];
	}
	// delete from background
	for (int i=int(m_BackElements.size())-1; i>=0; i--)
	{
		delete m_BackElements[i];
	}
	m_FrontElements.resize(0);
	m_BackElements.resize(0);
	for (unsigned int i=0; i<m_Paths.size(); i++)
	{
		delete m_Paths[i];
	}
	m_Paths.resize(0);

	for (unsigned int i=0; i<m_Waypoints.size(); i++)
	{
		delete m_Waypoints[i];
	}
	m_Waypoints.resize(0);

	if (m_TileA)
	{
		for (unsigned int i=0; i<(unsigned int)(m_Width * m_Height); i++)
		{
			m_TileA[i].Background.Invalidate();
		}
	}
}


BGWaypoint*	BGScene::AddWaypoint(const math::Vec2& Pos, int Index)
{
	if (Index < 0)
	{
		for (int i=1; i<MAX_WP_COUNT; i++)
		{
			Index = i;
			if (!GetWaypointFromID(i))
			{
				break;
			}
		}
	}
	BGWaypoint* pPoint = new BGWaypoint(Index);
	pPoint->SetPos(Pos);
	m_Waypoints.push_back(pPoint);
	//std::sort(m_Waypoints.begin(), m_Waypoints.end(), BGWaypoint::Sort);

	return pPoint;
}

BGWaypoint*	BGScene::GetWaypointFromID(int ID)
{
	for (unsigned int i=0; i<m_Waypoints.size(); i++)
	{
		if (ID == m_Waypoints[i]->GetID()) return m_Waypoints[i];
	}
	return 0;

}

BGWaypoint*	BGScene::GetWaypoint(const math::Vec2& Pos, bool Delete)
{
	math::Vec2	P = Pos - GetOffset();
	for (unsigned int i=0; i<m_Waypoints.size(); i++)
	{
		if (m_Waypoints[i]->IsInside(P, GetOffset()))
		{
			BGWaypoint*	pPoint = m_Waypoints[i];
			if (Delete)
			{
				m_Waypoints.erase(m_Waypoints.begin() + i);
			}
			return pPoint;
		}
	}
	return NULL;
}	

BGElement*			BGScene::GetCustomElement(const std::string& Name)
{
	return CustomComponentFactory::Instance().GetComponent(Name, this);

}
void				BGScene::RegisterComponent(const std::string& Name, CreateChunkCB pCreateCB, int KeyID, editor::EditorMain::CreateBrushCB pBrushCB)
{
	CustomComponentFactory::Instance().AddComponent(Name, KeyID, pCreateCB, pBrushCB);
}


void				BGScene::Register(LuaPlus::LuaState* _pScript)
{
#ifdef _ENABLE_LUABIND
//	BGEditor::Register(_pScript);
	// assume that the editor is accessed into script 
    lua_State * L = _pScript->GetCState ( );
	luabind::module(L)
    [
    luabind::class_<BGScene>( "BGScene" )
		.def("SetBackground",	&BGScene::SetBackground)
		.def("Save",			&BGScene::Save)
		.def("Load",			&BGScene::Load)
	];
#endif
}

